/*
 * Copyright (c) 2015, 2018, Oracle and/or its affiliates. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify it under
 * the terms of the GNU General Public License, version 2.0, as published by the
 * Free Software Foundation.
 *
 * This program is also distributed with certain software (including but not
 * limited to OpenSSL) that is licensed under separate terms, as designated in a
 * particular file or component or in included license documentation. The
 * authors of MySQL hereby grant you an additional permission to link the
 * program and your derivative works with the separately licensed software that
 * they have included with MySQL.
 *
 * Without limiting anything contained in the foregoing, this file, which is
 * part of MySQL Connector/J, is also subject to the Universal FOSS Exception,
 * version 1.0, a copy of which can be found at
 * http://oss.oracle.com/licenses/universal-foss-exception.
 *
 * This program is distributed in the hope that it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
 * FOR A PARTICULAR PURPOSE. See the GNU General Public License, version 2.0,
 * for more details.
 *
 * You should have received a copy of the GNU General Public License along with
 * this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301  USA
 */

package com.lzy.canal.enums;

import org.apache.commons.lang.StringUtils;

import java.math.BigDecimal;
import java.math.BigInteger;
import java.sql.Date;
import java.sql.Time;
import java.sql.Timestamp;

public enum MysqlTypeEnum {

    /**
     * DECIMAL[(M[,D])] [UNSIGNED] [ZEROFILL]
     * A packed "exact" fixed-point number. M is the total number of digits (the precision) and D is the number of digits
     * after the decimal point (the scale). The decimal point and (for negative numbers) the "-" sign are not counted in M.
     * If D is 0, values have no decimal point or fractional part. The maximum number of digits (M) for DECIMAL is 65.
     * The maximum number of supported decimals (D) is 30. If D is omitted, the default is 0. If M is omitted, the default is 10.
     * <p>
     * Protocol: FIELD_TYPE_DECIMAL = 0
     * Protocol: FIELD_TYPE_NEWDECIMAL = 246
     * <p>
     * These types are synonyms for DECIMAL:
     * DEC[(M[,D])] [UNSIGNED] [ZEROFILL],
     * NUMERIC[(M[,D])] [UNSIGNED] [ZEROFILL],
     * FIXED[(M[,D])] [UNSIGNED] [ZEROFILL]
     */
    DECIMAL("DECIMAL", BigDecimal.class),
    /**
     * DECIMAL[(M[,D])] UNSIGNED [ZEROFILL]
     *
     * @see MysqlTypeEnum#DECIMAL
     */
    DECIMAL_UNSIGNED("DECIMAL UNSIGNED", BigDecimal.class
    ),
    /**
     * TINYINT[(M)] [UNSIGNED] [ZEROFILL]
     * A very small integer. The signed range is -128 to 127. The unsigned range is 0 to 255.
     * <p>
     * Protocol: FIELD_TYPE_TINY = 1
     */
    TINYINT("TINYINT", Integer.class),
    /**
     * TINYINT[(M)] UNSIGNED [ZEROFILL]
     *
     * @see MysqlTypeEnum#TINYINT
     */
    TINYINT_UNSIGNED("TINYINT UNSIGNED", Integer.class
    ),
    /**
     * BOOL, BOOLEAN
     * These types are synonyms for TINYINT(1). A value of zero is considered false. Nonzero values are considered true
     * <p>
     * BOOLEAN is converted to TINYINT(1) during DDL execution i.e. it has the same precision=3. Thus we have to
     * look at full data type name and convert TINYINT to BOOLEAN (or BIT) if it has "(1)" length specification.
     * <p>
     * Protocol: FIELD_TYPE_TINY = 1
     */
    BOOLEAN("BOOLEAN", Boolean.class),
    /**
     * SMALLINT[(M)] [UNSIGNED] [ZEROFILL]
     * A small integer. The signed range is -32768 to 32767. The unsigned range is 0 to 65535.
     * <p>
     * Protocol: FIELD_TYPE_SHORT = 2
     */
    SMALLINT("SMALLINT", Integer.class),
    /**
     * SMALLINT[(M)] UNSIGNED [ZEROFILL]
     *
     * @see MysqlTypeEnum#SMALLINT
     */
    SMALLINT_UNSIGNED("SMALLINT UNSIGNED", Integer.class
    ),
    /**
     * INT[(M)] [UNSIGNED] [ZEROFILL]
     * A normal-size integer. The signed range is -2147483648 to 2147483647. The unsigned range is 0 to 4294967295.
     * <p>
     * Protocol: FIELD_TYPE_LONG = 3
     * <p>
     * INTEGER[(M)] [UNSIGNED] [ZEROFILL] is a synonym for INT.
     */
    INT("INT", Integer.class),
    /**
     * INT[(M)] UNSIGNED [ZEROFILL]
     *
     * @see MysqlTypeEnum#INT
     */
    INT_UNSIGNED("INT UNSIGNED", Long.class
    ),
    /**
     * FLOAT[(M,D)] [UNSIGNED] [ZEROFILL]
     * A small (single-precision) floating-point number. Permissible values are -3.402823466E+38 to -1.175494351E-38, 0,
     * and 1.175494351E-38 to 3.402823466E+38. These are the theoretical limits, based on the IEEE standard. The actual
     * range might be slightly smaller depending on your hardware or operating system.
     * <p>
     * M is the total number of digits and D is the number of digits following the decimal point. If M and D are omitted,
     * values are stored to the limits permitted by the hardware. A single-precision floating-point number is accurate to
     * approximately 7 decimal places.
     * <p>
     * Protocol: FIELD_TYPE_FLOAT = 4
     * <p>
     * Additionally:
     * FLOAT(p) [UNSIGNED] [ZEROFILL]
     * A floating-point number. p represents the precision in bits, but MySQL uses this value only to determine whether
     * to use FLOAT or DOUBLE for the resulting data type. If p is from 0 to 24, the data type becomes FLOAT with no M or D values.
     * If p is from 25 to 53, the data type becomes DOUBLE with no M or D values. The range of the resulting column is the same as
     * for the single-precision FLOAT or double-precision DOUBLE data types.
     */
    FLOAT("FLOAT", Float.class),
    /**
     * FLOAT[(M,D)] UNSIGNED [ZEROFILL]
     *
     * @see MysqlTypeEnum#FLOAT
     */
    FLOAT_UNSIGNED("FLOAT UNSIGNED", Float.class
    ),
    /**
     * DOUBLE[(M,D)] [UNSIGNED] [ZEROFILL]
     * A normal-size (double-precision) floating-point number. Permissible values are -1.7976931348623157E+308 to
     * -2.2250738585072014E-308, 0, and 2.2250738585072014E-308 to 1.7976931348623157E+308. These are the theoretical limits,
     * based on the IEEE standard. The actual range might be slightly smaller depending on your hardware or operating system.
     * <p>
     * M is the total number of digits and D is the number of digits following the decimal point. If M and D are omitted,
     * values are stored to the limits permitted by the hardware. A double-precision floating-point number is accurate to
     * approximately 15 decimal places.
     * <p>
     * Protocol: FIELD_TYPE_DOUBLE = 5
     * <p>
     * These types are synonyms for DOUBLE:
     * DOUBLE PRECISION[(M,D)] [UNSIGNED] [ZEROFILL],
     * REAL[(M,D)] [UNSIGNED] [ZEROFILL]. Exception: If the REAL_AS_FLOAT SQL mode is enabled, REAL is a synonym for FLOAT rather than DOUBLE.
     */
    DOUBLE("DOUBLE", Double.class),
    /**
     * DOUBLE[(M,D)] UNSIGNED [ZEROFILL]
     *
     * @see MysqlTypeEnum#DOUBLE
     */
    DOUBLE_UNSIGNED("DOUBLE UNSIGNED", Double.class
    ),
    /**
     * FIELD_TYPE_NULL = 6
     */
    NULL("NULL", Object.class),
    /**
     * TIMESTAMP[(fsp)]
     * A timestamp. The range is '1970-01-01 00:00:01.000000' UTC to '2038-01-19 03:14:07.999999' UTC.
     * TIMESTAMP values are stored as the number of seconds since the epoch ('1970-01-01 00:00:00' UTC).
     * A TIMESTAMP cannot represent the value '1970-01-01 00:00:00' because that is equivalent to 0 seconds
     * from the epoch and the value 0 is reserved for representing '0000-00-00 00:00:00', the "zero" TIMESTAMP value.
     * An optional fsp value in the range from 0 to 6 may be given to specify fractional seconds precision. A value
     * of 0 signifies that there is no fractional part. If omitted, the default precision is 0.
     * <p>
     * Protocol: FIELD_TYPE_TIMESTAMP = 7
     */
    // TODO If MySQL server run with the MAXDB SQL mode enabled, TIMESTAMP is identical with DATETIME. If this mode is enabled at the time that a table is created, TIMESTAMP columns are created as DATETIME columns.
    // As a result, such columns use DATETIME display format, have the same range of values, and there is no automatic initialization or updating to the current date and time
    TIMESTAMP("TIMESTAMP", Timestamp.class),
    /**
     * BIGINT[(M)] [UNSIGNED] [ZEROFILL]
     * A large integer. The signed range is -9223372036854775808 to 9223372036854775807. The unsigned range is 0 to 18446744073709551615.
     * <p>
     * Protocol: FIELD_TYPE_LONGLONG = 8
     * <p>
     * SERIAL is an alias for BIGINT UNSIGNED NOT NULL AUTO_INCREMENT UNIQUE.
     */
    BIGINT("BIGINT", Long.class),
    /**
     * BIGINT[(M)] UNSIGNED [ZEROFILL]
     *
     * @see MysqlTypeEnum#BIGINT
     */
    BIGINT_UNSIGNED("BIGINT UNSIGNED", BigInteger.class
    ),
    /**
     * MEDIUMINT[(M)] [UNSIGNED] [ZEROFILL]
     * A medium-sized integer. The signed range is -8388608 to 8388607. The unsigned range is 0 to 16777215.
     * <p>
     * Protocol: FIELD_TYPE_INT24 = 9
     */
    MEDIUMINT("MEDIUMINT", Integer.class),
    /**
     * MEDIUMINT[(M)] UNSIGNED [ZEROFILL]
     *
     * @see MysqlTypeEnum#MEDIUMINT
     */
    MEDIUMINT_UNSIGNED("MEDIUMINT UNSIGNED", Integer.class
    ),
    /**
     * DATE
     * A date. The supported range is '1000-01-01' to '9999-12-31'. MySQL displays DATE values in 'YYYY-MM-DD' format,
     * but permits assignment of values to DATE columns using either strings or numbers.
     * <p>
     * Protocol: FIELD_TYPE_DATE = 10
     */
    DATE("DATE", Date.class),
    /**
     * TIME[(fsp)]
     * A time. The range is '-838:59:59.000000' to '838:59:59.000000'. MySQL displays TIME values in
     * 'HH:MM:SS[.fraction]' format, but permits assignment of values to TIME columns using either strings or numbers.
     * An optional fsp value in the range from 0 to 6 may be given to specify fractional seconds precision. A value
     * of 0 signifies that there is no fractional part. If omitted, the default precision is 0.
     * <p>
     * Protocol: FIELD_TYPE_TIME = 11
     */
    TIME("TIME", Time.class),
    /**
     * DATETIME[(fsp)]
     * A date and time combination. The supported range is '1000-01-01 00:00:00.000000' to '9999-12-31 23:59:59.999999'.
     * MySQL displays DATETIME values in 'YYYY-MM-DD HH:MM:SS[.fraction]' format, but permits assignment of values to
     * DATETIME columns using either strings or numbers.
     * An optional fsp value in the range from 0 to 6 may be given to specify fractional seconds precision. A value
     * of 0 signifies that there is no fractional part. If omitted, the default precision is 0.
     * <p>
     * Protocol: FIELD_TYPE_DATETIME = 12
     */
    DATETIME("DATETIME", Timestamp.class),
    /**
     * YEAR[(4)]
     * A year in four-digit format. MySQL displays YEAR values in YYYY format, but permits assignment of
     * values to YEAR columns using either strings or numbers. Values display as 1901 to 2155, and 0000.
     * Protocol: FIELD_TYPE_YEAR = 13
     */
    YEAR("YEAR", Date.class),
    /**
     * [NATIONAL] VARCHAR(M) [CHARACTER SET charset_name] [COLLATE collation_name]
     * A variable-length string. M represents the maximum column length in characters. The range of M is 0 to 65,535.
     * The effective maximum length of a VARCHAR is subject to the maximum row size (65,535 bytes, which is shared among
     * all columns) and the character set used. For example, utf8 characters can require up to three bytes per character,
     * so a VARCHAR column that uses the utf8 character set can be declared to be a maximum of 21,844 characters.
     * <p>
     * MySQL stores VARCHAR values as a 1-byte or 2-byte length prefix plus data. The length prefix indicates the number
     * of bytes in the value. A VARCHAR column uses one length byte if values require no more than 255 bytes, two length
     * bytes if values may require more than 255 bytes.
     * <p>
     * Note
     * MySQL 5.7 follows the standard SQL specification, and does not remove trailing spaces from VARCHAR values.
     * <p>
     * VARCHAR is shorthand for CHARACTER VARYING. NATIONAL VARCHAR is the standard SQL way to define that a VARCHAR
     * column should use some predefined character set. MySQL 4.1 and up uses utf8 as this predefined character set.
     * NVARCHAR is shorthand for NATIONAL VARCHAR.
     * <p>
     * Protocol: FIELD_TYPE_VARCHAR = 15
     * Protocol: FIELD_TYPE_VAR_STRING = 253
     */
    VARCHAR("VARCHAR", String.class),
    /**
     * VARBINARY(M)
     * The VARBINARY type is similar to the VARCHAR type, but stores binary byte strings rather than nonbinary
     * character strings. M represents the maximum column length in bytes.
     * <p>
     * Protocol: FIELD_TYPE_VARCHAR = 15
     * Protocol: FIELD_TYPE_VAR_STRING = 253
     */
    VARBINARY("VARBINARY", null),
    /**
     * BIT[(M)]
     * A bit-field type. M indicates the number of bits per value, from 1 to 64. The default is 1 if M is omitted.
     * Protocol: FIELD_TYPE_BIT = 16
     */
    BIT("BIT", Boolean.class), // TODO maybe precision=8 ?
    /**
     * The size of JSON documents stored in JSON columns is limited to the value of the max_allowed_packet system variable (max value 1073741824).
     * (While the server manipulates a JSON value internally in memory, it can be larger; the limit applies when the server stores it.)
     * <p>
     * Protocol: FIELD_TYPE_BIT = 245
     */
    JSON("JSON", String.class),
    /**
     * ENUM('value1','value2',...) [CHARACTER SET charset_name] [COLLATE collation_name]
     * An enumeration. A string object that can have only one value, chosen from the list of values 'value1',
     * 'value2', ..., NULL or the special '' error value. ENUM values are represented internally as integers.
     * An ENUM column can have a maximum of 65,535 distinct elements. (The practical limit is less than 3000.)
     * A table can have no more than 255 unique element list definitions among its ENUM and SET columns considered as a group
     * <p>
     * Protocol: FIELD_TYPE_ENUM = 247
     */
    ENUM("ENUM", String.class),
    /**
     * SET('value1','value2',...) [CHARACTER SET charset_name] [COLLATE collation_name]
     * A set. A string object that can have zero or more values, each of which must be chosen from the list
     * of values 'value1', 'value2', ... SET values are represented internally as integers.
     * A SET column can have a maximum of 64 distinct members. A table can have no more than 255 unique
     * element list definitions among its ENUM and SET columns considered as a group
     * <p>
     * Protocol: FIELD_TYPE_SET = 248
     */
    SET("SET", String.class),
    /**
     * TINYBLOB
     * A BLOB column with a maximum length of 255 (28 ??? 1) bytes. Each TINYBLOB value is stored using a
     * 1-byte length prefix that indicates the number of bytes in the value.
     * <p>
     * Protocol:FIELD_TYPE_TINY_BLOB = 249
     */
    TINYBLOB("TINYBLOB", null),
    /**
     * TINYTEXT [CHARACTER SET charset_name] [COLLATE collation_name]
     * A TEXT column with a maximum length of 255 (28 ??? 1) characters. The effective maximum length
     * is less if the value contains multibyte characters. Each TINYTEXT value is stored using
     * a 1-byte length prefix that indicates the number of bytes in the value.
     * <p>
     * Protocol:FIELD_TYPE_TINY_BLOB = 249
     */
    TINYTEXT("TINYTEXT", String.class),
    /**
     * MEDIUMBLOB
     * A BLOB column with a maximum length of 16,777,215 (224 ??? 1) bytes. Each MEDIUMBLOB value is stored
     * using a 3-byte length prefix that indicates the number of bytes in the value.
     * <p>
     * Protocol: FIELD_TYPE_MEDIUM_BLOB = 250
     */
    MEDIUMBLOB("MEDIUMBLOB", null),
    /**
     * MEDIUMTEXT [CHARACTER SET charset_name] [COLLATE collation_name]
     * A TEXT column with a maximum length of 16,777,215 (224 ??? 1) characters. The effective maximum length
     * is less if the value contains multibyte characters. Each MEDIUMTEXT value is stored using a 3-byte
     * length prefix that indicates the number of bytes in the value.
     * <p>
     * Protocol: FIELD_TYPE_MEDIUM_BLOB = 250
     */
    MEDIUMTEXT("MEDIUMTEXT", String.class),
    /**
     * LONGBLOB
     * A BLOB column with a maximum length of 4,294,967,295 or 4GB (232 ??? 1) bytes. The effective maximum length
     * of LONGBLOB columns depends on the configured maximum packet size in the client/server protocol and available
     * memory. Each LONGBLOB value is stored using a 4-byte length prefix that indicates the number of bytes in the value.
     * <p>
     * Protocol: FIELD_TYPE_LONG_BLOB = 251
     */
    LONGBLOB("LONGBLOB", null),
    /**
     * LONGTEXT [CHARACTER SET charset_name] [COLLATE collation_name]
     * A TEXT column with a maximum length of 4,294,967,295 or 4GB (232 ??? 1) characters. The effective
     * maximum length is less if the value contains multibyte characters. The effective maximum length
     * of LONGTEXT columns also depends on the configured maximum packet size in the client/server protocol
     * and available memory. Each LONGTEXT value is stored using a 4-byte length prefix that indicates
     * the number of bytes in the value.
     * <p>
     * Protocol: FIELD_TYPE_LONG_BLOB = 251
     */
    LONGTEXT("LONGTEXT", String.class),
    /**
     * BLOB[(M)]
     * A BLOB column with a maximum length of 65,535 (216 ??? 1) bytes. Each BLOB value is stored using
     * a 2-byte length prefix that indicates the number of bytes in the value.
     * An optional length M can be given for this type. If this is done, MySQL creates the column as
     * the smallest BLOB type large enough to hold values M bytes long.
     * <p>
     * Protocol: FIELD_TYPE_BLOB = 252
     */
    BLOB("BLOB", null),
    /**
     * TEXT[(M)] [CHARACTER SET charset_name] [COLLATE collation_name]
     * A TEXT column with a maximum length of 65,535 (216 ??? 1) characters. The effective maximum length
     * is less if the value contains multibyte characters. Each TEXT value is stored using a 2-byte length
     * prefix that indicates the number of bytes in the value.
     * An optional length M can be given for this type. If this is done, MySQL creates the column as
     * the smallest TEXT type large enough to hold values M characters long.
     * <p>
     * Protocol: FIELD_TYPE_BLOB = 252
     */
    TEXT("TEXT", String.class),
    /**
     * [NATIONAL] CHAR[(M)] [CHARACTER SET charset_name] [COLLATE collation_name]
     * A fixed-length string that is always right-padded with spaces to the specified length when stored.
     * M represents the column length in characters. The range of M is 0 to 255. If M is omitted, the length is 1.
     * Note
     * Trailing spaces are removed when CHAR values are retrieved unless the PAD_CHAR_TO_FULL_LENGTH SQL mode is enabled.
     * CHAR is shorthand for CHARACTER. NATIONAL CHAR (or its equivalent short form, NCHAR) is the standard SQL way
     * to define that a CHAR column should use some predefined character set. MySQL 4.1 and up uses utf8
     * as this predefined character set.
     * <p>
     * MySQL permits you to create a column of type CHAR(0). This is useful primarily when you have to be compliant
     * with old applications that depend on the existence of a column but that do not actually use its value.
     * CHAR(0) is also quite nice when you need a column that can take only two values: A column that is defined
     * as CHAR(0) NULL occupies only one bit and can take only the values NULL and '' (the empty string).
     * <p>
     * Protocol: FIELD_TYPE_STRING = 254
     */
    CHAR("CHAR", String.class),
    /**
     * BINARY(M)
     * The BINARY type is similar to the CHAR type, but stores binary byte strings rather than nonbinary character strings.
     * M represents the column length in bytes.
     * <p>
     * The CHAR BYTE data type is an alias for the BINARY data type.
     * <p>
     * Protocol: no concrete type on the wire TODO: really?
     */
    BINARY("BINARY", null),
    /**
     * Top class for Spatial Data Types
     * <p>
     * Protocol: FIELD_TYPE_GEOMETRY = 255
     */
    GEOMETRY("GEOMETRY", null), // TODO check precision, it isn't well documented, only mentioned that WKB format is represented by BLOB 
    /**
     * Fall-back type for those MySQL data types which c/J can't recognize.
     * Handled the same as BLOB.
     * <p>
     * Has no protocol ID.
     */
    UNKNOWN("UNKNOWN", null);

    /**
     * Get MysqlType matching the full MySQL type name, for example "DECIMAL(5,3) UNSIGNED ZEROFILL".
     * Distinct *_UNSIGNED type will be returned if "UNSIGNED" is present in fullMysqlTypeName.
     *
     * @param fullMysqlTypeName full MySQL type name
     * @return MysqlType
     */
    public static MysqlTypeEnum getByName(String fullMysqlTypeName) {
        String typeName = "";

        if (fullMysqlTypeName.contains("(")) {
            typeName = fullMysqlTypeName.substring(0, fullMysqlTypeName.indexOf("(")).trim();
        } else {
            typeName = fullMysqlTypeName;
        }

        // the order of checks is important because some short names could match parts of longer names
        if (StringUtils.indexOfIgnoreCase(typeName, "DECIMAL") != -1 || StringUtils.indexOfIgnoreCase(typeName, "DEC") != -1
                || StringUtils.indexOfIgnoreCase(typeName, "NUMERIC") != -1 || StringUtils.indexOfIgnoreCase(typeName, "FIXED") != -1) {
            return StringUtils.indexOfIgnoreCase(fullMysqlTypeName, "UNSIGNED") != -1 ? DECIMAL_UNSIGNED : DECIMAL;

        }
        // IMPORTANT: "TINYBLOB" must be checked before "TINY"

        if (StringUtils.indexOfIgnoreCase(typeName, "TINYBLOB") != -1) {
            return TINYBLOB;

        }
        // IMPORTANT: "TINYTEXT" must be checked before "TINY"
        if (StringUtils.indexOfIgnoreCase(typeName, "TINYTEXT") != -1) {
            return TINYTEXT;

        }
        // TODO BOOLEAN is a synonym for TINYINT(1)
        if (StringUtils.indexOfIgnoreCase(typeName, "TINYINT") != -1 || StringUtils.indexOfIgnoreCase(typeName, "TINY") != -1
                || StringUtils.indexOfIgnoreCase(typeName, "INT1") != -1) {
            return StringUtils.indexOfIgnoreCase(fullMysqlTypeName, "UNSIGNED") != -1 ? TINYINT_UNSIGNED : TINYINT;
        }

        if (StringUtils.indexOfIgnoreCase(typeName, "MEDIUMINT") != -1
                // IMPORTANT: "INT24" must be checked before "INT2"
                || StringUtils.indexOfIgnoreCase(typeName, "INT24") != -1 || StringUtils.indexOfIgnoreCase(typeName, "INT3") != -1
                || StringUtils.indexOfIgnoreCase(typeName, "MIDDLEINT") != -1) {
            return StringUtils.indexOfIgnoreCase(fullMysqlTypeName, "UNSIGNED") != -1 ? MEDIUMINT_UNSIGNED : MEDIUMINT;
        }
        if (StringUtils.indexOfIgnoreCase(typeName, "SMALLINT") != -1 || StringUtils.indexOfIgnoreCase(typeName, "INT2") != -1) {
            return StringUtils.indexOfIgnoreCase(fullMysqlTypeName, "UNSIGNED") != -1 ? SMALLINT_UNSIGNED : SMALLINT;

        }
        if (StringUtils.indexOfIgnoreCase(typeName, "BIGINT") != -1 || StringUtils.indexOfIgnoreCase(typeName, "SERIAL") != -1
                || StringUtils.indexOfIgnoreCase(typeName, "INT8") != -1) {
            // SERIAL is an alias for BIGINT UNSIGNED NOT NULL AUTO_INCREMENT UNIQUE.
            return StringUtils.indexOfIgnoreCase(fullMysqlTypeName, "UNSIGNED") != -1 ? BIGINT_UNSIGNED : BIGINT;

        }
        if (StringUtils.indexOfIgnoreCase(typeName, "POINT") != -1) {
            // also covers "MULTIPOINT"
            // IMPORTANT: "POINT" must be checked before "INT"
            return GEOMETRY; // TODO think about different MysqlTypes for Spatial Data Types

        }
        if (StringUtils.indexOfIgnoreCase(typeName, "INT") != -1 || StringUtils.indexOfIgnoreCase(typeName, "INTEGER") != -1
                || StringUtils.indexOfIgnoreCase(typeName, "INT4") != -1) {
            // IMPORTANT: "INT" must be checked after all "*INT*" types
            return StringUtils.indexOfIgnoreCase(fullMysqlTypeName, "UNSIGNED") != -1 ? INT_UNSIGNED : INT;

        }
        if (StringUtils.indexOfIgnoreCase(typeName, "DOUBLE") != -1 || StringUtils.indexOfIgnoreCase(typeName, "REAL") != -1
                // IMPORTANT: "FLOAT8" must be checked before "FLOAT"
                || StringUtils.indexOfIgnoreCase(typeName, "FLOAT8") != -1) {
            // TODO Exception: If the REAL_AS_FLOAT SQL mode is enabled, REAL is a synonym for FLOAT rather than DOUBLE.
            return StringUtils.indexOfIgnoreCase(fullMysqlTypeName, "UNSIGNED") != -1 ? DOUBLE_UNSIGNED : DOUBLE;
        }
        if (StringUtils.indexOfIgnoreCase(typeName, "FLOAT") != -1) {
            // TODO FLOAT(p) [UNSIGNED] [ZEROFILL]. If p is from 0 to 24, the data type becomes FLOAT with no M or D values. If p is from 25 to 53, the data type becomes DOUBLE with no M or D values.
            return StringUtils.indexOfIgnoreCase(fullMysqlTypeName, "UNSIGNED") != -1 ? FLOAT_UNSIGNED : FLOAT;
        }
        if (StringUtils.indexOfIgnoreCase(typeName, "NULL") != -1) {
            return NULL;
        }
        if (StringUtils.indexOfIgnoreCase(typeName, "TIMESTAMP") != -1) {
            // IMPORTANT: "TIMESTAMP" must be checked before "TIME"
            return TIMESTAMP;
        }
        if (StringUtils.indexOfIgnoreCase(typeName, "DATETIME") != -1) {
            // IMPORTANT: "DATETIME" must be checked before "DATE" and "TIME"
            return DATETIME;
        }
        if (StringUtils.indexOfIgnoreCase(typeName, "DATE") != -1) {
            return DATE;
        }
        if (StringUtils.indexOfIgnoreCase(typeName, "TIME") != -1) {
            return TIME;

        }
        if (StringUtils.indexOfIgnoreCase(typeName, "YEAR") != -1) {
            return YEAR;

        }
        if (StringUtils.indexOfIgnoreCase(typeName, "LONGBLOB") != -1) {
            // IMPORTANT: "LONGBLOB" must be checked before "LONG" and "BLOB"
            return LONGBLOB;

        }
        if (StringUtils.indexOfIgnoreCase(typeName, "LONGTEXT") != -1) {
            // IMPORTANT: "LONGTEXT" must be checked before "LONG" and "TEXT"
            return LONGTEXT;

        }
        if (StringUtils.indexOfIgnoreCase(typeName, "MEDIUMBLOB") != -1 || StringUtils.indexOfIgnoreCase(typeName, "LONG VARBINARY") != -1) {
            // IMPORTANT: "MEDIUMBLOB" must be checked before "BLOB"
            // IMPORTANT: "LONG VARBINARY" must be checked before "LONG" and "VARBINARY"
            return MEDIUMBLOB;

        }
        if (StringUtils.indexOfIgnoreCase(typeName, "MEDIUMTEXT") != -1 || StringUtils.indexOfIgnoreCase(typeName, "LONG VARCHAR") != -1
                || StringUtils.indexOfIgnoreCase(typeName, "LONG") != -1) {
            // IMPORTANT: "MEDIUMTEXT" must be checked before "TEXT"
            // IMPORTANT: "LONG VARCHAR" must be checked before "VARCHAR"
            return MEDIUMTEXT;

        }
        if (StringUtils.indexOfIgnoreCase(typeName, "VARCHAR") != -1 || StringUtils.indexOfIgnoreCase(typeName, "NVARCHAR") != -1
                || StringUtils.indexOfIgnoreCase(typeName, "NATIONAL VARCHAR") != -1 || StringUtils.indexOfIgnoreCase(typeName, "CHARACTER VARYING") != -1) {
            // IMPORTANT: "CHARACTER VARYING" must be checked before "CHARACTER" and "CHAR"
            return VARCHAR;

        }
        if (StringUtils.indexOfIgnoreCase(typeName, "VARBINARY") != -1) {
            return VARBINARY;

        }
        if (StringUtils.indexOfIgnoreCase(typeName, "BINARY") != -1 || StringUtils.indexOfIgnoreCase(typeName, "CHAR BYTE") != -1) {
            // IMPORTANT: "BINARY" must be checked after all "*BINARY" types
            // IMPORTANT: "CHAR BYTE" must be checked before "CHAR"
            return BINARY;

        }
        if (StringUtils.indexOfIgnoreCase(typeName, "LINESTRING") != -1) {
            // also covers "MULTILINESTRING"
            // IMPORTANT: "LINESTRING" must be checked before "STRING"
            return GEOMETRY;

        }
        if (StringUtils.indexOfIgnoreCase(typeName, "STRING") != -1
                // IMPORTANT: "CHAR" must be checked after all "*CHAR*" types
                || StringUtils.indexOfIgnoreCase(typeName, "CHAR") != -1 || StringUtils.indexOfIgnoreCase(typeName, "NCHAR") != -1
                || StringUtils.indexOfIgnoreCase(typeName, "NATIONAL CHAR") != -1 || StringUtils.indexOfIgnoreCase(typeName, "CHARACTER") != -1) {
            return CHAR;

        }
        if (StringUtils.indexOfIgnoreCase(typeName, "BOOLEAN") != -1 || StringUtils.indexOfIgnoreCase(typeName, "BOOL") != -1) {
            return BOOLEAN;

        }
        if (StringUtils.indexOfIgnoreCase(typeName, "BIT") != -1) {
            return BIT;

        }
        if (StringUtils.indexOfIgnoreCase(typeName, "JSON") != -1) {
            return JSON;

        }
        if (StringUtils.indexOfIgnoreCase(typeName, "ENUM") != -1) {
            return ENUM;

        }
        if (StringUtils.indexOfIgnoreCase(typeName, "SET") != -1) {
            return SET;

        }
        if (StringUtils.indexOfIgnoreCase(typeName, "BLOB") != -1) {
            return BLOB;

        }
        if (StringUtils.indexOfIgnoreCase(typeName, "TEXT") != -1) {
            return TEXT;
        }
        // covers "GEOMETRY", "GEOMETRYCOLLECTION" and "GEOMCOLLECTION"
        if (StringUtils.indexOfIgnoreCase(typeName, "GEOM") != -1
                // also covers "MULTIPOINT"
                || StringUtils.indexOfIgnoreCase(typeName, "POINT") != -1
                // also covers "MULTIPOLYGON"
                || StringUtils.indexOfIgnoreCase(typeName, "POLYGON") != -1) {
            // TODO think about different MysqlTypes for Spatial Data Types
            return GEOMETRY;
        }
        return UNKNOWN;
    }

    private final String name;
    protected final Class<?> javaClass;

    /**
     * @param mysqlTypeName mysqlTypeName
     * @param javaClass     javaClass
     */
    MysqlTypeEnum(String mysqlTypeName, Class<?> javaClass) {
        this.name = mysqlTypeName;
        this.javaClass = javaClass;
    }

    public String getName() {
        return this.name;
    }

    public String getClassName() {
        if (this.javaClass == null) {
            return "[B";
        }
        return this.javaClass.getName();
    }


}
